import Constant from './Constant'
import Common from './Common'
import Utils from './Utils'

// 添加EventBus监听
export function eventBusListener(self) {
  // 需要使用eventBus
  const eventBus = self.modeler.get('eventBus')
  // 需要监听的事件集合
  const eventTypes = [
    'element.click',
    'element.changed',
    'selection.changed',
    'editorActions.init',
    'diagram.init',
    'elements.create',
    'elements.move',
    'elements.delete',
    'elements.distribute',
    'elements.align',
    'elements.click',
    'elements.changed',
    'element.updateAttachment',
    'create.move', // 创建shape移动时触发
    'create.end', // 创建shape结束时触发
    'label.create',
    'shape.added', // 添加shape时触发
    'shape.create',
    'shape.resize',
    'shape.append',
    'shape.delete',
    'shape.move',
    'shape.changed',
    'shape.move.init', // 移动shape初始时触发
    'shape.move.move', // 移动shape时触发
    'shape.move.end', // 移动shape结束时触发
    'shape.remove', // 删除shape时触发
    'shape.removed', // 删除shape结束时触发
    'shape.attach',
    'shape.replace',
    'connect.start', // 开始connect时触发
    'connect.end', // 结束connect时触发
    'connect.move', // 移动connect时触发
    'connect.hover', // connect悬停节点变化时触发
    'connect.out', //
    'connect.cleanup', // 取消connect时触发
    'connection.create', // 创建connection时触发
    'connection.start',
    'connection.added', // 添加connection时触发
    'connection.remove', // 删除connection时触发
    'connection.removed', // 删除connection结束时触发
    'connection.changed', // connection改变时触发
    'connection.updateWaypoints',
    'connection.reconnect',
    'connection.layout',
    'connection.delete',
    'connection.move',
    'drag.move',
    'drag.init',
    'drag.start',
    'drag.cleanup',
    'bendpoint.move.move',
    'bendpoint.move.start',
    'bendpoint.move.cancel'
  ]
  eventTypes.forEach(eventType => {
    eventBus.on(eventType, event => {
      console.log(eventType, event)
      // 在drag事件初始化时触发
      if (eventType === 'drag.init') {
        dragInitEvent(self, event, self.model)
      }
      // 创建shape移动时触发
      if (eventType === 'create.move') {
        createMoveEvent(self, event)
      }
      // connection改变时触发
      if (eventType === 'connection.changed') {
        connectionChangedEvent(self, event)
      }
      // 结束connect时触发
      if (eventType === 'connect.end') {
        connectEndEvent(self, event)
      }
      // 点击element时触发
      if (eventType === 'element.click') {
        const element = event.element
        if (Utils.isShapeElement(element)) {
          Utils.updateElement(self.modeler, element, { '_model': self.model })
        }
      }
    })
  })
}

/**
 * 是否超出进度限制数
 *
 * @param {*} targetShape
 */
function isIncomingOut(targetShape) {
  const targetStage = targetShape.businessObject.$attrs.stage
  const inLength = targetShape.incoming.length // 已经存在进度数
  const inLimit = Common.getInLimit(targetStage) // 进度限制数
  // 超出进度限制
  if (inLimit && inLength > 0 && inLimit < inLength) {
    return true
  }
  return false
}
/**
 * 是否超出出度限制数
 *
 * @param {*} sourceShape
 */
function isOutgoingOut(sourceShape) {
  const sourceStage = sourceShape.businessObject.$attrs.stage
  const outLength = sourceShape.outgoing.length // 已经存在出度数
  const outLimit = Common.getOutLimit(sourceStage) // 出度限制数
  // 超出出度限制
  if (outLimit && outLength > 0 && outLimit < outLength) {
    return true
  }
  return false
}

/**
 * 判断是否重复关联
 *
 * @param {*} connection
 */
function isRepeatedlyConnect(connection) {
  const targetIds = {}
  const sourceShape = connection.source
  const connections = sourceShape.outgoing
  for (let i = 0; i < connections.length; i++) {
    const connection = connections[i]
    const targetId = connection.target.id
    if (!targetIds[targetId]) {
      targetIds[targetId] = 1
    } else {
      targetIds[targetId] += 1
      return true
    }
  }
  return false
}

/**
 * 是否关联自己
 *
 * @param {*} connection
 */
function isConnectSelf(connection) {
  const sourceId = connection.source.id
  const targetId = connection.target.id
  // 源ID和目标ID一致
  if (sourceId === targetId) {
    return true
  }
  return false
}

/**
 * 目标节点类型是否禁止关联
 *
 * @param {*} targetShape
 */
function isTargetShapeTypeForbidConnect(targetShape) {
  const targetType = targetShape.businessObject.$attrs.type
  // 连线的结束节点的类型是源类型
  if (targetType === Constant.TYPE_SOURCE) {
    return true
  }
  return false
}

/**
 * 源节点类型是否禁止关联
 *
 * @param {*} sourceShape
 */
function isSourceShapeTypeForbidConnect(sourceShape) {
  const sourceType = sourceShape.businessObject.$attrs.type
  // 连线的起始节点的类型是目标类型
  if (sourceType === Constant.TYPE_TARGET) {
    return true
  }
  return false
}

/**
 * 是否流程有多个源类型节点
 *
 * @param {*} process
 * @param {*} shape
 */
function isProcessMultiSourceTypeShape(process, shape) {
  const businessObject = shape.businessObject
  const type = businessObject.$attrs.type
  // 如果是源类型
  if (type === Constant.TYPE_SOURCE) {
    const flowElements = process.flowElements // shape和connection集合
    if (flowElements) {
      let count = 0
      for (let i = 0; i < flowElements.length; i++) {
        const element = flowElements[i]
        if (element.$attrs.type === Constant.TYPE_SOURCE) {
          count++
          // 已经存在有其他源类型的节点
          if (count >= 1) {
            return true
          }
        }
      }
    }
  }
  return false
}

/**
 * 是否不是有向无环图
 *
 * @param {*} connection
 * @param {*} outConnection
 */
function isNotDAG(connection, outConnection) {
  const targetShape = outConnection.target
  const connections = targetShape.outgoing
  for (let i = 0; i < connections.length; i++) {
    const outConn = connections[i]
    if (outConn.id === connection.id) {
      return true
    } else {
      if (isNotDAG(connection, outConn)) {
        return true
      }
    }
  }
  return false
}

/**
 * 结束connect时触发的事件
 *
 * @param {*} self
 * @param {*} event
 */
export function connectEndEvent(self, event) {
  const sourceShape = event.shape
  const targetShape = event.hover
  // 更新Shape参数已达到触发重新渲染Shape的效果
  Utils.updateElement(self.modeler, sourceShape, {
    '_refresh': !sourceShape.businessObject.$attrs._refresh
  })
  Utils.updateElement(self.modeler, targetShape, {
    '_refresh': !targetShape.businessObject.$attrs._refresh
  })
}

/**
 * connection改变时触发的事件
 *
 * @param {*} self
 * @param {*} event
 */
export function connectionChangedEvent(self, event) {
  const connection = event.element
  const sourceShape = connection.source
  const targetShape = connection.target
  let error = false
  const messages = []
  // 判断是否关联自己
  if (isConnectSelf(connection)) {
    error = true
    messages.push('禁止关联自己')
  }
  // 判断是否重复关联
  if (isRepeatedlyConnect(connection)) {
    error = true
    messages.push('节点不能重复关联')
  }
  // 判断源节点类型是否禁止关联
  if (isSourceShapeTypeForbidConnect(sourceShape)) {
    error = true
    messages.push(sourceShape.id + ' - 是目标类型节点无法关联')
  }
  // 判断目标节点类型是否禁止关联
  if (isTargetShapeTypeForbidConnect(targetShape)) {
    error = true
    messages.push(targetShape.id + ' - 是源类型节点无法被关联')
  }
  // 判断是否超出出度限制数
  if (isOutgoingOut(sourceShape)) {
    error = true
    messages.push(targetShape.id + ' - 超出最大关联限制数')
  }
  // 判断是否超出进度限制数
  if (isIncomingOut(targetShape)) {
    error = true
    messages.push(targetShape.id + ' - 超出最大被关联限制数')
  }
  // 判断是有向无环图
  if (isNotDAG(connection, connection)) {
    error = true
    messages.push('流程必须是有向无环图')
  }
  if (error) {
    Utils.removeElement(self.modeler, connection)
    Utils.message(self, messages, 'error')
  }
}

/**
 * 创建shape移动时触发的事件
 *
 * @param {*} self
 * @param {*} event
 */
export function createMoveEvent(self, event) {
  const shape = event.shape
  let error = false
  const messages = []
  // 判断是否流程有多个源类型节点
  const process = self.modeler._definitions.rootElements[0] // Process
  if (isProcessMultiSourceTypeShape(process, shape)) {
    error = true
    messages.push('源已经存在')
  }
  if (error) {
    Utils.removeElement(self.modeler, shape)
    Utils.message(self, messages, 'error')
  }
}

/**
 * 在drag事件初始化时触发
 *
 * @param {*} self
 * @param {*} event
 */
export function dragInitEvent(self, event) {
  // 禁止connection的拖动
  if (self.model !== 'edit' && event.connection) {
    self.modeler.get('dragging').cancel(true)
  }
}

export default { eventBusListener }
